package com.chen.utils.handler;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.builder.Entity;
import com.baomidou.mybatisplus.generator.config.builder.Mapper;
import com.baomidou.mybatisplus.generator.config.builder.Service;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.chen.base.utils.PropertyUtil;
import com.chen.utils.params.GenerateCodeParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Collections;

/**
 * 数据库实体代码生成工具类
 *
 * @author ChenJinLong*/
@Component
public class GenerateJavaFileHandler {

    @Value("${db-column-type-config:}")
    private String columnConfig;

    /**
     * 生成数据库实体的相关java文件
     * @param param 参数
     * @return 文件输出路径
     */
    public String generatorFile(GenerateCodeParam param){
        String outPath = PropertyUtil.getProperty("temp-dir") + UUID.fastUUID();
        // 数据库连接配置
        DataSourceConfig.Builder dsBuilder = new DataSourceConfig.Builder(
                param.getUrl(), param.getUsername(), param.getPassword())
                .typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                    // 自定义数据类型处理器
                    // todo com.baomidou.mybatisplus.generator.config.rules.DbColumnType
                    IColumnType columnType = typeRegistry.getColumnType(metaInfo);
                    for (String conStr : columnConfig.split(",")) {
                        String[] types = conStr.split("-");
                        if (types.length == 2 && columnType.getType().equals(types[0])){
                            for (DbColumnType dbColumnType : DbColumnType.values()) {
                                if (dbColumnType.getType().equals(types[1])){
                                    return dbColumnType;
                                }
                            }
                        }
                    }
                    return columnType;
                });
        FastAutoGenerator autoGenerator = FastAutoGenerator.create(dsBuilder);

        // 全局配置
        globalConfig(param, autoGenerator, outPath);

        // 包配置
        packageConfig(param, autoGenerator, outPath);

        // 策略配置
        strategyConfig(param, autoGenerator);

        // 模板文件配置
        templateConfig(autoGenerator);

        // 注入自定义(DTO/VO)文件配置
        injectionConfig(autoGenerator);

        // 使用Freemarker引擎模板，默认的是Velocity引擎模板
        autoGenerator.templateEngine(new FreemarkerTemplateEngine()).execute();

        return outPath;
    }

    /**
     * 注入自定义(DTO/VO)文件配置
     */
    private void injectionConfig(FastAutoGenerator autoGenerator) {
        autoGenerator.injectionConfig(consumer -> {
            // dto
            CustomFile dtoFile = new CustomFile.Builder().enableFileOverride()
                    .templatePath("/ftl/myDTO.java.ftl")
                    .fileName("DTO.java")
                    .packageName("/oclass/dto").build();
            // vo
            CustomFile voFile = new CustomFile.Builder().enableFileOverride()
                    .templatePath("/ftl/myVO.java.ftl")
                    .fileName("VO.java")
                    .packageName("/oclass/vo").build();

            consumer.customFile(dtoFile);
            consumer.customFile(voFile);
        });
    }

    /**
     * 模板文件配置
     */
    private void templateConfig(FastAutoGenerator autoGenerator) {
        autoGenerator.templateConfig(builder -> {
            builder.disable()
                    .xml("/ftl/myMapper.xml")
                    .mapper("/ftl/myMapper.java")
                    .service("/ftl/myService.java")
                    .serviceImpl("/ftl/myServiceImpl.java")
                    .controller("/ftl/myController.java")
                    .entity("/ftl/myEntity.java");
        });
    }

    /**
     * 策略配置
     */
    private void strategyConfig(GenerateCodeParam param, FastAutoGenerator autoGenerator) {
        autoGenerator.strategyConfig(builder -> {
            builder.addInclude(param.getTableNameList()); // 设置需要生成的表名
            if (CollectionUtil.isNotEmpty(param.getIgnoreTablePrefixs())){
                builder.addTablePrefix(param.getIgnoreTablePrefixs()); // 设置过滤表前缀
            }

            // controller
            builder.controllerBuilder()
                    .enableFileOverride() // 覆盖已生成文件
                    .enableRestStyle(); // 开启@RestController

            // mapper
            Mapper.Builder mapperBuilder = builder.mapperBuilder()
                    .enableBaseResultMap() // 开启xml通用映射map
                    .enableFileOverride() // 覆盖已生成文件
                    .enableMapperAnnotation(); // 开启@mapper注解
            if (StrUtil.isNotBlank(param.getSuperMapperClass())){
                mapperBuilder.superClass(param.getSuperMapperClass()); // Mapper接口的父类
            }

            // service
            Service.Builder serviceBuilder = builder.serviceBuilder();
            serviceBuilder
                    // 格式化 service 文件名称
                    .formatServiceFileName("%sService")
                    .formatServiceImplFileName("%sServiceImpl")
                    .enableFileOverride();// 覆盖已生成文件
            if (StrUtil.isNotBlank(param.getSuperServiceImplClass())){
                serviceBuilder.superServiceImplClass(param.getSuperServiceImplClass()); // serviceImpl的父类
            }

            // entity
            Entity.Builder entityBuilder = builder.entityBuilder();
            entityBuilder
                    .enableFileOverride()// 覆盖已生成文件
                    .disableSerialVersionUID()// 禁用生成 serialVersionUID
                    //.enableActiveRecord()// 开启ActiveRecord模型：extends Model<T>
                    .logicDeleteColumnName(param.getLogicDeleteColumnName())// 指定逻辑删除的字段名称
                    .versionColumnName(param.getVersionColumnName())// 指定版本字段的名称
                    .addTableFills(param.getAddTableFills().toArray(new Column[0]))
                    .enableTableFieldAnnotation();// 开启@TableField("")注解说明
            if (param.getEnableLombok()){
                entityBuilder.enableLombok(); // 开启lombok
                //.enableChainModel(); // 开启@Accessors(chain = true)
            }
            if (BooleanUtil.isTrue(param.getIgnoreBooleanPrefix())){
                entityBuilder.enableRemoveIsPrefix();
            }
        });
    }

    /**
     * 包配置
     */
    private void packageConfig(GenerateCodeParam param, FastAutoGenerator autoGenerator, String outPath) {
        autoGenerator.packageConfig(builder -> {
            // 设置mapperXml生成路径
            builder.pathInfo(Collections.singletonMap(OutputFile.xml, outPath + "/xml"));
            // 设置父包名
            builder.parent(param.getParentPath());

            // Entity包名
            if (StrUtil.isNotBlank(param.getEntityPack())){
                builder.entity(param.getEntityPack());
            }
            // Service包名
            if (StrUtil.isNotBlank(param.getServicePack())){
                builder.service(param.getServicePack());
            }
            // ServiceImpl包名
            if (StrUtil.isNotBlank(param.getServiceImplPack())){
                builder.serviceImpl(param.getServiceImplPack());
            }
            // Mapper包名
            if (StrUtil.isNotBlank(param.getMapperPack())){
                builder.mapper(param.getMapperPack());
            }
        });
    }

    /**
     * 全局配置
     */
    private void globalConfig(GenerateCodeParam param, FastAutoGenerator autoGenerator, String outPath) {
        autoGenerator.globalConfig(builder -> {
            builder.author(param.getAuthor()) // 设置作者
                    .disableOpenDir() // 生成完毕禁止自动打开文件
                    .commentDate("yyyy-MM-dd hh:mm:ss")
                    .dateType(DateType.TIME_PACK)//定义生成的实体类中日期的类型 TIME_PACK=LocalDateTime;ONLY_DATE=Date;
                    .outputDir(outPath); // 指定输出目录
            if (param.getEnableSwagger()){
                builder.enableSwagger(); // 开启 swagger 模式
            }
        });
    }
}
